home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / libgimp / gimpfileselection.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-08-21  |  12.6 KB  |  461 lines

  1. /* LIBGIMP - The GIMP Library 
  2.  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
  3.  *
  4.  * gimpfileselection.c
  5.  * Copyright (C) 1999-2000 Michael Natterer <mitch@gimp.org>
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library is distributed in the hope that it will be useful, 
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  15.  * Library General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the
  19.  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  20.  * Boston, MA 02111-1307, USA.
  21.  */
  22. #include "config.h"
  23.  
  24. #include <glib.h>         /* Needed here by Win32 gcc compilation */
  25.  
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #ifdef HAVE_UNISTD_H
  29. #include <unistd.h>
  30. #endif
  31. #include <string.h>
  32.  
  33. #include <gtk/gtk.h>
  34.  
  35. #include "gimpfileselection.h"
  36.  
  37. #include "libgimp-intl.h"
  38.  
  39. #include "pixmaps/yes.xpm"
  40. #include "pixmaps/no.xpm"
  41.  
  42.  
  43. #ifdef G_OS_WIN32
  44. # ifndef S_ISDIR
  45. #  define S_ISDIR(m) ((m) & _S_IFDIR)
  46. # endif
  47. # ifndef S_ISREG
  48. #  define S_ISREG(m) ((m) & _S_IFREG)
  49. # endif
  50. #endif
  51.  
  52. /*  callbacks  */
  53. static void gimp_file_selection_realize                  (GtkWidget *widget);
  54. static void gimp_file_selection_entry_callback           (GtkWidget *widget,
  55.                               gpointer   data);
  56. static gint gimp_file_selection_entry_focus_out_callback (GtkWidget *widget,
  57.                               GdkEvent  *event,
  58.                               gpointer   data);
  59. static void gimp_file_selection_browse_callback          (GtkWidget *widget,
  60.                               gpointer   data);
  61.  
  62. /*  private functions  */
  63. static void gimp_file_selection_check_filename (GimpFileSelection *gfs);
  64.  
  65. enum
  66. {
  67.   FILENAME_CHANGED,
  68.   LAST_SIGNAL
  69. };
  70.  
  71. static guint gimp_file_selection_signals[LAST_SIGNAL] = { 0 };
  72.  
  73. static GtkHBoxClass *parent_class = NULL;
  74.  
  75. static void
  76. gimp_file_selection_destroy (GtkObject *object)
  77. {
  78.   GimpFileSelection *gfs;
  79.  
  80.   g_return_if_fail (object != NULL);
  81.   g_return_if_fail (GIMP_IS_FILE_SELECTION (object));
  82.  
  83.   gfs = GIMP_FILE_SELECTION (object);
  84.  
  85.   if (gfs->file_selection)
  86.     gtk_widget_destroy (gfs->file_selection);
  87.  
  88.   if (gfs->title)
  89.     g_free (gfs->title);
  90.  
  91.   if (gfs->yes_pixmap)
  92.     gdk_pixmap_unref (gfs->yes_pixmap);
  93.   if (gfs->yes_mask)
  94.     gdk_bitmap_unref (gfs->yes_mask);
  95.  
  96.   if (gfs->no_pixmap)
  97.     gdk_pixmap_unref (gfs->no_pixmap);
  98.   if (gfs->no_mask)
  99.     gdk_bitmap_unref (gfs->no_mask);
  100.  
  101.   if (GTK_OBJECT_CLASS (parent_class)->destroy)
  102.     GTK_OBJECT_CLASS (parent_class)->destroy (object);
  103. }
  104.  
  105. static void
  106. gimp_file_selection_class_init (GimpFileSelectionClass *class)
  107. {
  108.   GtkObjectClass *object_class;
  109.   GtkWidgetClass *widget_class;
  110.  
  111.   object_class = (GtkObjectClass *) class;
  112.   widget_class = (GtkWidgetClass *) class;
  113.  
  114.   parent_class = gtk_type_class (gtk_hbox_get_type ());
  115.  
  116.   gimp_file_selection_signals[FILENAME_CHANGED] = 
  117.     gtk_signal_new ("filename_changed",
  118.             GTK_RUN_FIRST,
  119.             object_class->type,
  120.             GTK_SIGNAL_OFFSET (GimpFileSelectionClass,
  121.                        filename_changed),
  122.             gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  123.  
  124.   gtk_object_class_add_signals (object_class, gimp_file_selection_signals, 
  125.                 LAST_SIGNAL);
  126.  
  127.   class->filename_changed = NULL;
  128.  
  129.   object_class->destroy = gimp_file_selection_destroy;
  130.   widget_class->realize = gimp_file_selection_realize;
  131. }
  132.  
  133. static void
  134. gimp_file_selection_init (GimpFileSelection *gfs)
  135. {
  136.   gfs->title          = NULL;
  137.   gfs->file_selection = NULL;
  138.   gfs->check_valid    = FALSE;
  139.  
  140.   gfs->file_exists    = NULL;
  141.   gfs->yes_pixmap     = NULL;
  142.   gfs->yes_mask       = NULL;
  143.   gfs->no_pixmap      = NULL;
  144.   gfs->no_mask        = NULL;
  145.  
  146.   gtk_box_set_spacing (GTK_BOX (gfs), 2);
  147.   gtk_box_set_homogeneous (GTK_BOX (gfs), FALSE);
  148.  
  149.   gfs->browse_button = gtk_button_new_with_label (" ... ");
  150.   gtk_box_pack_end (GTK_BOX (gfs), gfs->browse_button, FALSE, FALSE, 0);
  151.   gtk_signal_connect (GTK_OBJECT(gfs->browse_button), "clicked",
  152.               GTK_SIGNAL_FUNC (gimp_file_selection_browse_callback),
  153.               gfs);
  154.   gtk_widget_show (gfs->browse_button);
  155.  
  156.   gfs->entry = gtk_entry_new ();
  157.   gtk_box_pack_end (GTK_BOX (gfs), gfs->entry, TRUE, TRUE, 0);
  158.   gtk_signal_connect (GTK_OBJECT (gfs->entry), "activate",
  159.               GTK_SIGNAL_FUNC (gimp_file_selection_entry_callback),
  160.               gfs);
  161.   gtk_signal_connect (GTK_OBJECT (gfs->entry), "focus_out_event",
  162.               GTK_SIGNAL_FUNC (gimp_file_selection_entry_focus_out_callback),
  163.               gfs);
  164.   gtk_widget_show (gfs->entry);
  165. }
  166.  
  167. GtkType
  168. gimp_file_selection_get_type (void)
  169. {
  170.   static GtkType gfs_type = 0;
  171.  
  172.   if (!gfs_type)
  173.     {
  174.       GtkTypeInfo gfs_info =
  175.       {
  176.     "GimpFileSelection",
  177.     sizeof (GimpFileSelection),
  178.     sizeof (GimpFileSelectionClass),
  179.     (GtkClassInitFunc) gimp_file_selection_class_init,
  180.     (GtkObjectInitFunc) gimp_file_selection_init,
  181.     /* reserved_1 */ NULL,
  182.     /* reserved_2 */ NULL,
  183.         (GtkClassInitFunc) NULL
  184.       };
  185.  
  186.       gfs_type = gtk_type_unique (gtk_hbox_get_type (), &gfs_info);
  187.     }
  188.   
  189.   return gfs_type;
  190. }
  191.  
  192. /**
  193.  * gimp_file_selection_new:
  194.  * @title: The title of the #GtkFileSelection dialog.
  195.  * @filename: The initial filename.
  196.  * @dir_only: #TRUE if the file selection should accept directories only.
  197.  * @check_valid: #TRUE if the widget should check if the entered file
  198.  *               really exists.
  199.  *
  200.  * Creates a new #GimpFileSelection widget.
  201.  *
  202.  * Returns: A pointer to the new #GimpFileSelection widget.
  203.  **/
  204. GtkWidget *
  205. gimp_file_selection_new (const gchar *title,
  206.              const gchar *filename,
  207.              gboolean     dir_only,
  208.              gboolean     check_valid)
  209. {
  210.   GimpFileSelection *gfs;
  211.  
  212.   gfs = gtk_type_new (gimp_file_selection_get_type ());
  213.  
  214.   gfs->title       = g_strdup (title);
  215.   gfs->dir_only    = dir_only;
  216.   gfs->check_valid = check_valid;
  217.  
  218.   gimp_file_selection_set_filename (gfs, filename);
  219.  
  220.   return GTK_WIDGET (gfs);
  221. }
  222.  
  223. /**
  224.  * gimp_file_selection_get_filename:
  225.  * @gfs: The file selection you want to know the filename from.
  226.  *
  227.  * Note that you have to g_free() the returned string.
  228.  *
  229.  * Returns: The file or directory the user has entered.
  230.  **/
  231. gchar *
  232. gimp_file_selection_get_filename (GimpFileSelection *gfs)
  233. {
  234.   g_return_val_if_fail (gfs != NULL, g_strdup (""));
  235.   g_return_val_if_fail (GIMP_IS_FILE_SELECTION (gfs), g_strdup (""));
  236.  
  237.   return gtk_editable_get_chars (GTK_EDITABLE (gfs->entry), 0, -1);
  238. }
  239.  
  240. /**
  241.  * gimp_file_selection_set_filename:
  242.  * @gfs: The file selection you want to set the filename for.
  243.  * @filename: The new filename.
  244.  *
  245.  * If you specified @check_valid as #TRUE in gimp_file_selection_new()
  246.  * the #GimpFileSelection will immediately check the validity of the file
  247.  * name.
  248.  *
  249.  */
  250. void
  251. gimp_file_selection_set_filename (GimpFileSelection *gfs,
  252.                   const gchar       *filename)
  253. {
  254.   g_return_if_fail (gfs != NULL);
  255.   g_return_if_fail (GIMP_IS_FILE_SELECTION (gfs));
  256.  
  257.   gtk_entry_set_text (GTK_ENTRY (gfs->entry), filename ? filename : "");
  258.  
  259.   /*  update everything
  260.    */
  261.   gimp_file_selection_entry_callback (gfs->entry, (gpointer) gfs);
  262. }
  263.  
  264. static void
  265. gimp_file_selection_realize (GtkWidget *widget)
  266. {
  267.   GimpFileSelection *gfs;
  268.   GtkStyle          *style;
  269.  
  270.   gfs = GIMP_FILE_SELECTION (widget);
  271.  
  272.   if (GTK_WIDGET_CLASS (parent_class)->realize)
  273.     (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
  274.  
  275.   if (! gfs->check_valid)
  276.     return;
  277.  
  278.   style = gtk_widget_get_style (widget);
  279.  
  280.   gfs->yes_pixmap = gdk_pixmap_create_from_xpm_d (widget->window,
  281.                           &gfs->yes_mask,
  282.                           &style->bg[GTK_STATE_NORMAL],
  283.                           yes_xpm);
  284.   gfs->no_pixmap = gdk_pixmap_create_from_xpm_d (widget->window,
  285.                          &gfs->no_mask,
  286.                          &style->bg[GTK_STATE_NORMAL],
  287.                          no_xpm);
  288.  
  289.   gfs->file_exists = gtk_pixmap_new (gfs->no_pixmap, gfs->no_mask);
  290.   gtk_box_pack_start (GTK_BOX (gfs), gfs->file_exists, FALSE, FALSE, 0);
  291.  
  292.   gimp_file_selection_check_filename (gfs);
  293.   gtk_widget_show (gfs->file_exists);
  294. }
  295.  
  296. static void
  297. gimp_file_selection_entry_callback (GtkWidget *widget,
  298.                     gpointer   data)
  299. {
  300.   GimpFileSelection *gfs;
  301.   gchar             *filename;
  302.   gint               len;
  303.  
  304.   gfs = GIMP_FILE_SELECTION (data);
  305.  
  306.   /*  filenames still need more sanity checking
  307.    *  (erase double G_DIR_SEPARATORS, ...)
  308.    */
  309.   filename = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
  310.   filename = g_strstrip (filename);
  311.  
  312.   while (((len = strlen (filename)) > 1) &&
  313.      (filename[len - 1] == G_DIR_SEPARATOR))
  314.     filename[len - 1] = '\0';
  315.  
  316.   gtk_signal_handler_block_by_data (GTK_OBJECT (gfs->entry), gfs);
  317.   gtk_entry_set_text (GTK_ENTRY (gfs->entry), filename);
  318.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (gfs->entry), gfs);
  319.  
  320.   if (gfs->file_selection)
  321.     gtk_file_selection_set_filename (GTK_FILE_SELECTION (gfs->file_selection),
  322.                      filename);
  323.   g_free (filename);
  324.  
  325.   gimp_file_selection_check_filename (gfs);
  326.  
  327.   gtk_entry_set_position (GTK_ENTRY (gfs->entry), -1);
  328.  
  329.   gtk_signal_emit (GTK_OBJECT (gfs),
  330.            gimp_file_selection_signals[FILENAME_CHANGED]);
  331. }
  332.  
  333. static gboolean
  334. gimp_file_selection_entry_focus_out_callback (GtkWidget *widget,
  335.                           GdkEvent  *event,
  336.                           gpointer   data)
  337. {
  338.   gimp_file_selection_entry_callback (widget, data);
  339.  
  340.   return TRUE;
  341. }
  342.  
  343. /*  local callbacks of gimp_file_selection_browse_callback()  */
  344. static void
  345. gimp_file_selection_filesel_ok_callback (GtkWidget *widget,
  346.                      gpointer   data)
  347. {
  348.   GimpFileSelection *gfs;
  349.   gchar             *filename;
  350.  
  351.   gfs = GIMP_FILE_SELECTION (data);
  352.   filename =
  353.     gtk_file_selection_get_filename (GTK_FILE_SELECTION (gfs->file_selection));
  354.  
  355.   gtk_entry_set_text (GTK_ENTRY (gfs->entry), filename);
  356.  
  357.   gtk_widget_hide (gfs->file_selection);
  358.  
  359.   /*  update everything  */
  360.   gimp_file_selection_entry_callback (gfs->entry, data);
  361. }
  362.  
  363. static void
  364. gimp_file_selection_browse_callback (GtkWidget *widget,
  365.                      gpointer   data)
  366. {
  367.   GimpFileSelection *gfs;
  368.   gchar             *filename;
  369.  
  370.   gfs = GIMP_FILE_SELECTION (data);
  371.   filename = gtk_editable_get_chars (GTK_EDITABLE (gfs->entry), 0, -1);
  372.   
  373.   if (gfs->file_selection == NULL)
  374.     {
  375.       if (gfs->dir_only)
  376.     {
  377.       gfs->file_selection = gtk_file_selection_new (gfs->title);
  378.  
  379.       /*  hiding these widgets uses internal gtk+ knowledge, but it's
  380.        *  easier than creating my own directory browser -- michael
  381.        */
  382.       gtk_widget_hide
  383.         (GTK_FILE_SELECTION (gfs->file_selection)->fileop_del_file);
  384.       gtk_widget_hide
  385.         (GTK_FILE_SELECTION (gfs->file_selection)->file_list->parent);
  386.     }
  387.       else
  388.     {
  389.       gfs->file_selection = gtk_file_selection_new (_("Select File"));
  390.     }
  391.  
  392.       gtk_window_set_position (GTK_WINDOW (gfs->file_selection),
  393.                    GTK_WIN_POS_MOUSE);
  394.       gtk_window_set_wmclass (GTK_WINDOW (gfs->file_selection),
  395.                   "file_select", "Gimp");
  396.  
  397.       /* slightly compress the dialog */
  398.       gtk_container_set_border_width (GTK_CONTAINER (gfs->file_selection), 2);
  399.       gtk_container_set_border_width (GTK_CONTAINER (GTK_FILE_SELECTION (gfs->file_selection)->button_area), 2);
  400.  
  401.       gtk_signal_connect 
  402.     (GTK_OBJECT (GTK_FILE_SELECTION (gfs->file_selection)->ok_button),
  403.      "clicked",
  404.      GTK_SIGNAL_FUNC (gimp_file_selection_filesel_ok_callback),
  405.      gfs);
  406.       gtk_signal_connect
  407.     (GTK_OBJECT (GTK_FILE_SELECTION (gfs->file_selection)->selection_entry),
  408.      "activate",
  409.      GTK_SIGNAL_FUNC (gimp_file_selection_filesel_ok_callback),
  410.      gfs);
  411.  
  412.       gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (gfs->file_selection)->cancel_button),
  413.                  "clicked",
  414.                  GTK_SIGNAL_FUNC (gtk_widget_hide),
  415.                  GTK_OBJECT (gfs->file_selection));
  416.       gtk_signal_connect_object (GTK_OBJECT (gfs), "unmap",
  417.                  GTK_SIGNAL_FUNC (gtk_widget_hide),
  418.                  GTK_OBJECT (gfs->file_selection));
  419.       gtk_signal_connect_object (GTK_OBJECT (gfs->file_selection),
  420.                  "delete_event",
  421.                  GTK_SIGNAL_FUNC (gtk_widget_hide),
  422.                  GTK_OBJECT (gfs->file_selection));
  423.     }
  424.  
  425.   gtk_file_selection_set_filename (GTK_FILE_SELECTION (gfs->file_selection),
  426.                    filename);
  427.  
  428.   if (! GTK_WIDGET_VISIBLE (gfs->file_selection))
  429.     gtk_widget_show (gfs->file_selection);
  430.   else
  431.     gdk_window_raise (gfs->file_selection->window);
  432. }
  433.  
  434. static void
  435. gimp_file_selection_check_filename (GimpFileSelection *gfs)
  436. {
  437.   static struct stat  statbuf;
  438.   gchar              *filename;
  439.  
  440.   if (! gfs->check_valid)
  441.     return;
  442.  
  443.   if (gfs->file_exists == NULL)
  444.     return;
  445.  
  446.   filename = gtk_editable_get_chars (GTK_EDITABLE (gfs->entry), 0, -1);
  447.   if ((stat (filename, &statbuf) == 0) &&
  448.       (gfs->dir_only ? S_ISDIR (statbuf.st_mode) : S_ISREG (statbuf.st_mode)))
  449.     {
  450.       gtk_pixmap_set (GTK_PIXMAP (gfs->file_exists),
  451.               gfs->yes_pixmap, gfs->yes_mask);
  452.     }
  453.   else
  454.     {
  455.       gtk_pixmap_set (GTK_PIXMAP (gfs->file_exists),
  456.               gfs->no_pixmap, gfs->no_mask);
  457.     }
  458.  
  459.   g_free (filename);
  460. }
  461.